import pandas as pd
import numpy as np

from sklearn.model_selection import train_test_split
# 线性回归算法
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt

from keras.datasets import mnist
# 导入keras.utils工具库的类别转换工具
from keras.utils import to_categorical

# 导入Keras模型，以及各种神经网络的层
from keras import models
from keras.layers import Dense, Dropout, Flatten, Conv2D, MaxPooling2D

# 1.问题定义
# 2.数据的收集和预处理
# 3.模型算法的选择
# 4.选择机器学习模型
# 5.超参数调试和性能优化

# 需要安装tensorflow: pip3 install tensorflow -i https://pypi.tuna.tsinghua.edu.cn/simple --user

def ml_demo():
    df_housing = pd.read_csv("house-master/house.csv")
    # 特征集X大写 标签集y小写  通过特征集X预测房价 标签集y
    X = df_housing.drop("median_house_value", axis = 1)
    y = df_housing.median_house_value

    # 拆分数据集，80%用于训练，20%用于验证
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=0)
    model = LinearRegression() # 确定线性回归算法
    model.fit(X_train, y_train) # 根据训练集数据，训练机器，拟合函数

    y_pred = model.predict(X_test) # 预测验证集的y值
    print('房价的真值（测试集）', y_test)
    print('预测的房价（测试集）', y_pred)
    # 评估预测结果 Sklearn线性回归模型的score属性给出的是R2分数，给出的是预测值的方差与总体方差之间的差异
    print("给预测评分: ", model.score(X_test, y_test))

    # 用散点图显示家庭收入中位数和房价中位数的分布
    plt.scatter(X_test.median_income, y_test, color='brown')
    # 画出回归函数，从特征到预测标签
    plt.plot(X_test.median_income, y_pred, color='green', linewidth=1)
    # x轴：家庭收入中位数
    plt.xlabel("Median Income")
    # y轴：房价中位数
    plt.ylabel("Median House Value")
    # 显示房价分布和函数模型
    plt.show()

def keras_mnist_demo():
    # 读入测试训练集和测试集
    (X_train_image, y_train_lable), (X_test_image, y_test_lable) = mnist.load_data()
    print("数据集张量形状: ", X_train_image.shape) # 用shape方法显示张量的形状
    print("第一个数据样本: \n", X_train_image[0]) #python的索引从0开始
    print("第一个数据样本的标签: ", y_train_lable[0])

    # 数据格式转换 给标签增加一个维度 Keras要求图像数据集导入卷积网络模型时为4阶张量，最后一阶代表颜色深度，灰度图像只有一个颜色通道，可以设置其为1
    X_train = X_train_image.reshape(60000, 28, 28, 1)
    X_test = X_test_image.reshape(10000, 28, 28, 1)
    y_train = to_categorical(y_train_lable, 10)
    y_test = to_categorical(y_test_lable, 10)

    print("训练集张量形状: ", X_train.shape)
    print("第一个数据标签: ", y_train[0])

    # 模型选择：
    # 1. 线性模型（线性回归，逻辑回归）
    # 2. 非线性模型（支持向量机，k最邻近分类）
    # 3. 基于树和集成的模型（决策树，随机森林，梯度提升树）
    # 4. 神经网络（人工神经网络，卷积神经网络，长短期记忆网络等）

    model = models.Sequential() # 用序贯方式建立模型
    model.add(Conv2D(32, (3,3), activation='relu', # 添加Conv2D层（二维卷积）
                     input_shape=(28, 28, 1))) # 指定输入数据样本张量的类型
    model.add(MaxPooling2D(pool_size=(2, 2))) # 添加MaxPooling2D层（最大化池）
    model.add(Conv2D(64, (3,3), activation='relu')) # 添加Conv2D层（二维卷积）
    model.add(MaxPooling2D(pool_size=(2,2))) # 添加MaxPooling2D层（最大化池）
    model.add(Dropout(0.25)) # 添加Dropout层（防止过拟合）
    model.add(Flatten()) # 展平
    model.add(Dense(128, activation='relu')) # 添加全连接层
    model.add(Dropout(0.5)) # 添加Dropout层（防止过拟合）
    model.add(Dense(10, activation='softmax')) # Softmax分类激活，输出10维分类码，输出预测标签y值

    #编译模型
    model.compile(optimizer='rmsprop', # 指定优化器
                  loss='categorical_crossentropy', # 指定损失函数
                  metrics=['accuracy'] # 指定验证过程中的评估指标
                  )

    # fit拟合，开始5论训练
    model.fit(X_train, y_train, # 指定训练特征集，训练标签集
              validation_split=0.3, # 部分训练集数据拆分成验证集
              epochs=5, # 循环轮次为5轮
              batch_size=128 # 以128为批量进行训练
              )
    # accuracy 代表训练集上的预测准确率，val_accuracy 代表验证集上的预测准确率

    # 模型评估:
    # 1.损失函数：内部参数的评估方法
    # 2.验证：R2分数，均方误差函数，平均绝对误差函数，交叉熵函数等

    # 训练集进行模型训练，验证集进行模型评估，找到最后的参数与超参数后在测试集进行测试，确定模型
    # 过拟合：模型对当前数据集的针对性过强，泛化能力弱；
    # 信息泄露：调试超参数本身也是训练，导致对验证集过拟合

    #查看预测结果
    pred = model.predict(X_test[0].reshape(1, 28, 28, 1)) # 预测测试集第一个数据
    print(pred[0], "转换一下格式得到: ", pred.argmax())  # 把one-hot编码转换成数字

    plt.imshow(X_test[0].reshape(28, 28), cmap='Greys') # 输出这个图片
    plt.show()



if __name__ == '__main__':
    # ml_demo()
    keras_mnist_demo();
